package com.laicunba.service.accounting;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;

import com.laicunba.dao.GenericQueryDao;
import com.laicunba.pojo.AccountBalanceSheetDO;
import com.laicunba.pojo.AccountDO;
import com.laicunba.service.AccountBalanceSheetService;
import com.laicunba.service.AccountService;
import com.laicunba.service.AccountService.ACCOUNTING_SUBJECT;
import com.laicunba.service.AccountService.RECHARGE_WAY;
import com.laicunba.util.AssertUtil;
import com.laicunba.util.LogUtil;
import com.laicunba.util.SessionUtil;
import com.laicunba.util.StringUtil;

/**
 * 记账模版
 * 
 * @author why
 *
 */
public class AccountingTemplate {
	private static final Logger LOG = Logger.getLogger(AccountingTemplate.class);
	private final static char SIGN_SPLIT_WORD = '&';
	@Autowired
	private AccountBalanceSheetService accountBalanceSheetService;
	@Autowired
	private GenericQueryDao queryDao;
	@Autowired
	private AccountService accountService;

	protected boolean theWholeBalanceIsZero() {
		return true;
		//return BigDecimal.ZERO.compareTo(accountService.totalBalance()) == 0;
	}

	/**
	 * 平台借记
	 */
	protected String makePlatformDr(ACCOUNTING_SUBJECT drSubject, BigDecimal amount, String certificateId, String ext, String remark) {
		AccountDO drAccount = getAccount(drSubject);
		AssertUtil.assertNotNull(drAccount);
		AccountBalanceSheetDO preBalanceSheet = getPreBalanceSheet(drAccount.getId());
		AccountBalanceSheetDO balanceSheet = new AccountBalanceSheetDO();
		balanceSheet.setAccountId(drAccount.getId());
		balanceSheet.setCertificateId(certificateId);
		balanceSheet.setRemark(remark);
		balanceSheet.setExt(ext);
		balanceSheet.setRowNum(preBalanceSheet == null ? 1 : preBalanceSheet.getRowNum() + 1);
		balanceSheet.setBalance(preBalanceSheet == null ? amount : preBalanceSheet.getBalance().add(amount));
		balanceSheet.setDr(amount);
		balanceSheet.setOptUserId(SessionUtil.CURRENT_USER_ID.get());
		balanceSheet.setRid(SessionUtil.RID.get());
		balanceSheet.setSign(signBalanceSheet(balanceSheet, preBalanceSheet));
		drAccount.setBalance(drAccount.getBalance() == null ? amount : drAccount.getBalance().add(amount));
		AssertUtil.assertTrue(drAccount.getBalance().compareTo(balanceSheet.getBalance()) == 0);
		AssertUtil.assertTrue(accountBalanceSheetService.create(balanceSheet));

		drAccount.setBalanceSheetId(balanceSheet.getId());
		drAccount.setOptUserId(balanceSheet.getOptUserId());
		drAccount.setDr(balanceSheet.getDr());
		drAccount.setRowNum(balanceSheet.getRowNum());
		drAccount.setCertificateId(balanceSheet.getCertificateId());
		drAccount.setCr(balanceSheet.getCr());
		drAccount.setExt(balanceSheet.getExt());
		drAccount.setSign(balanceSheet.getSign());
		AssertUtil.assertTrue(accountService.updateSkipCheckOwner(drAccount));
		return balanceSheet.getSign();
	}

	/**
	 * 平台借记
	 */
	protected String makePlatformCr(ACCOUNTING_SUBJECT crSubject, BigDecimal amount, String certificateId, String ext, String remark) {
		AccountDO crAccount = getAccount(crSubject);
		AssertUtil.assertNotNull(crAccount);
		AccountBalanceSheetDO preBalanceSheet = getPreBalanceSheet(crAccount.getId());
		AccountBalanceSheetDO balanceSheet = new AccountBalanceSheetDO();
		balanceSheet.setAccountId(crAccount.getId());
		balanceSheet.setCertificateId(certificateId);
		balanceSheet.setRemark(remark);
		balanceSheet.setExt(ext);
		balanceSheet.setRowNum(preBalanceSheet == null ? 1 : preBalanceSheet.getRowNum() + 1);
		balanceSheet.setBalance(preBalanceSheet == null ? amount : preBalanceSheet.getBalance().add(amount));
		balanceSheet.setCr(amount);
		balanceSheet.setOptUserId(SessionUtil.CURRENT_USER_ID.get());
		balanceSheet.setRid(SessionUtil.RID.get());
		balanceSheet.setSign(signBalanceSheet(balanceSheet, preBalanceSheet));
		crAccount.setBalance(crAccount.getBalance() == null ? amount : crAccount.getBalance().add(amount));
		AssertUtil.assertTrue(crAccount.getBalance().compareTo(balanceSheet.getBalance()) == 0);
		AssertUtil.assertTrue(accountBalanceSheetService.create(balanceSheet));

		crAccount.setBalanceSheetId(balanceSheet.getId());
		crAccount.setOptUserId(balanceSheet.getOptUserId());
		crAccount.setDr(balanceSheet.getDr());
		crAccount.setRowNum(balanceSheet.getRowNum());
		crAccount.setCertificateId(balanceSheet.getCertificateId());
		crAccount.setCr(balanceSheet.getCr());
		crAccount.setExt(balanceSheet.getExt());
		crAccount.setSign(balanceSheet.getSign());
		AssertUtil.assertTrue(accountService.updateSkipCheckOwner(crAccount));
		return balanceSheet.getSign();
	}

	/**
	 * 用户贷记
	 */
	protected String makeUserCr(String userId, ACCOUNTING_SUBJECT crSubject, BigDecimal amount, String certificateId, String ext, String remark) {
		AccountDO crAccount = getAccount(userId, crSubject);
		AssertUtil.assertNotNull(crAccount);
		AccountBalanceSheetDO preBalanceSheet = getPreBalanceSheet(crAccount.getId());
		AccountBalanceSheetDO balanceSheet = new AccountBalanceSheetDO();
		balanceSheet.setAccountId(crAccount.getId());
		balanceSheet.setCertificateId(certificateId);
		balanceSheet.setRemark(remark);
		balanceSheet.setExt(ext);
		balanceSheet.setRowNum(preBalanceSheet == null ? 1 : preBalanceSheet.getRowNum() + 1);
		balanceSheet.setBalance(preBalanceSheet == null ? amount : preBalanceSheet.getBalance().add(amount));
		balanceSheet.setCr(amount);
		balanceSheet.setOptUserId(SessionUtil.CURRENT_USER_ID.get());
		balanceSheet.setRid(SessionUtil.RID.get());
		balanceSheet.setSign(signBalanceSheet(balanceSheet, preBalanceSheet));
		crAccount.setBalance(crAccount.getBalance() == null ? amount : crAccount.getBalance().add(amount));
		AssertUtil.assertTrue(crAccount.getBalance().compareTo(balanceSheet.getBalance()) == 0);
		AssertUtil.assertTrue(accountBalanceSheetService.create(balanceSheet));

		// 回填Account
		crAccount.setBalanceSheetId(balanceSheet.getId());
		crAccount.setOptUserId(balanceSheet.getOptUserId());
		crAccount.setDr(balanceSheet.getDr());
		crAccount.setRowNum(balanceSheet.getRowNum());
		crAccount.setCertificateId(balanceSheet.getCertificateId());
		crAccount.setCr(balanceSheet.getCr());
		crAccount.setExt(balanceSheet.getExt());
		crAccount.setSign(balanceSheet.getSign());
		AssertUtil.assertTrue(accountService.updateSkipCheckOwner(crAccount));
		return balanceSheet.getSign();
	}

	/**
	 * 用户借记
	 */
	protected String makeUserDr(String userId, ACCOUNTING_SUBJECT drSubject, BigDecimal amount, String certificateId, String ext, String remark) {
		AccountDO drAccount = getAccount(userId, drSubject);
		AssertUtil.assertNotNull(drAccount);
		AccountBalanceSheetDO preBalanceSheet = getPreBalanceSheet(drAccount.getId());

		AccountBalanceSheetDO balanceSheet = new AccountBalanceSheetDO();
		balanceSheet.setAccountId(drAccount.getId());
		balanceSheet.setCertificateId(certificateId);
		balanceSheet.setRemark(remark);
		balanceSheet.setExt(ext);
		balanceSheet.setRowNum(preBalanceSheet == null ? 1 : preBalanceSheet.getRowNum() + 1);
		balanceSheet.setBalance(preBalanceSheet == null ? amount : preBalanceSheet.getBalance().add(amount));
		balanceSheet.setDr(amount);
		balanceSheet.setOptUserId(SessionUtil.CURRENT_USER_ID.get());
		balanceSheet.setRid(SessionUtil.RID.get());
		balanceSheet.setSign(signBalanceSheet(balanceSheet, preBalanceSheet));

		drAccount.setBalance(drAccount.getBalance() == null ? amount : drAccount.getBalance().add(amount));
		AssertUtil.assertTrue(drAccount.getBalance().compareTo(balanceSheet.getBalance()) == 0);
		AssertUtil.assertTrue(accountBalanceSheetService.create(balanceSheet));
		// 回填Account
		drAccount.setBalanceSheetId(balanceSheet.getId());
		drAccount.setOptUserId(balanceSheet.getOptUserId());
		drAccount.setDr(balanceSheet.getDr());
		drAccount.setRowNum(balanceSheet.getRowNum());
		drAccount.setCertificateId(balanceSheet.getCertificateId());
		drAccount.setCr(balanceSheet.getCr());
		drAccount.setExt(balanceSheet.getExt());
		drAccount.setSign(balanceSheet.getSign());
		AssertUtil.assertTrue(accountService.updateSkipCheckOwner(drAccount));
		return balanceSheet.getSign();
	}

	/**
	 * 签名一行数据，使得该行数据重要字段不能修改
	 */
	protected String signBalanceSheet(AccountBalanceSheetDO balanceSheet, AccountBalanceSheetDO preBalanceSheet) {
		// 签名算法:RowNum,AccountId,CertificateId,Balance,Dr,Cr,Rid,OptUserId,前一条数据Sign
		StringBuffer sb = new StringBuffer();
		sb.append(balanceSheet.getRowNum()).append(SIGN_SPLIT_WORD);
		sb.append(balanceSheet.getAccountId()).append(SIGN_SPLIT_WORD);
		sb.append(balanceSheet.getCertificateId()).append(SIGN_SPLIT_WORD);
		sb.append(balanceSheet.getBalance()).append(SIGN_SPLIT_WORD);
		if (balanceSheet.getDr() != null && balanceSheet.getDr().compareTo(BigDecimal.ZERO) == 1) {
			sb.append(balanceSheet.getDr()).append(SIGN_SPLIT_WORD);
		} else {
			sb.append(balanceSheet.getCr()).append(SIGN_SPLIT_WORD);
		}
		sb.append(balanceSheet.getRid()).append(SIGN_SPLIT_WORD);
		sb.append(balanceSheet.getOptUserId()).append(SIGN_SPLIT_WORD);
		if (preBalanceSheet != null) {
			sb.append(preBalanceSheet.getSign()).append(SIGN_SPLIT_WORD);
		}
		LOG.info(LogUtil.format("tag:SIGN_BALANCE_SHEET", sb.toString()));
		return StringUtil.md5(sb.toString());
	}

	/**
	 * 得到上一条账单
	 * 
	 * @return
	 */
	protected AccountBalanceSheetDO getPreBalanceSheet(String accountId) {

		return queryDao.selectOne("ACCOUNT_BALANCE_SHEET.getPreBalanceSheet", accountId);
	}

	private AccountDO getAccount(ACCOUNTING_SUBJECT drSubject) {
		Map map = new HashMap();
		map.put("subjectCode", drSubject.getCode());
		AccountDO acc = queryDao.selectOne("ACCOUNT.getPlatformAccount", map);
		if (acc != null) {
			return acc;
		}
		acc = new AccountDO();
		acc.setAccountType("platform");
		acc.setBalance(BigDecimal.ZERO);
		acc.setAccountNo(StringUtil.genGUID());
		acc.setRowNum(0);
		acc.setSubjectCode(drSubject.getCode());
		acc.setSubjectName(drSubject.getName());
		acc.setSubjectType(drSubject.isAssets() ? "A" : "L");
		AssertUtil.assertTrue(accountService.create(acc));
		return acc;
	}

	protected AccountDO getAccount(String userId, ACCOUNTING_SUBJECT drSubject) {
		Map map = new HashMap();
		map.put("userId", userId);
		map.put("subjectCode", drSubject.getCode());
		AccountDO acc = queryDao.selectOne("ACCOUNT.getAccount", map);
		if (acc != null) {
			return acc;
		}
		acc = new AccountDO();
		acc.setUserId(userId);
		acc.setAccountType("investor");
		acc.setBalance(BigDecimal.ZERO);
		acc.setAccountNo(StringUtil.genGUID());
		acc.setRowNum(0);
		acc.setSubjectCode(drSubject.getCode());
		acc.setSubjectName(drSubject.getName());
		acc.setSubjectType(drSubject.isAssets() ? "A" : "L");
		AssertUtil.assertTrue(accountService.create(acc));
		return acc;
	}
}
